#include "Avalon/IOTests/MockClientConnection.hpp"
#include "Avalon/IO/ConnectException.hpp"
#include "Avalon/IO/PipedReader.hpp"
#include "Avalon/IO/PipedWriter.hpp"
#include "Avalon/IOTests/MockClientChannel.hpp"
#include "Avalon/IOTests/MockServerConnection.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::IO::Tests;
using namespace Avalon::Threading;
using namespace boost;

MockClientConnection::MockClientConnection(MockServerConnection* server,
    MockClientChannel* channel, const shared_ptr<PipedReader>& serverReader,
    const shared_ptr<PipedReader>& clientReader)
    : m_server(server),
      m_channel(channel),
      m_serverReader(serverReader),
      m_clientReader(clientReader),
      m_isOpen(false) {}

MockClientConnection::~MockClientConnection() {
  Close();
}

void MockClientConnection::Open() {
  unique_lock<recursive_mutex> lock(m_mutex);
  if(m_isOpen) {
    return;
  }
  if(!m_server->AddPendingConnection(this)) {
    m_connectedCondition.wait(lock);
  }
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(ConnectException("Connection rejected."));
  }
}

bool MockClientConnection::IsOpen() const {
  lock_guard<recursive_mutex> lock(m_mutex);
  return m_isOpen;
}

void MockClientConnection::Close() {
  lock_guard<recursive_mutex> lock(m_mutex);
  if(!m_isOpen) {
    return;
  }
  m_server->RemoveConnection(this);
  CloseConnection();
}

void MockClientConnection::SetClosedSlot(const ClosedSlot& slot,
    Out<bool> isOpen) {
  lock_guard<recursive_mutex> lock(m_mutex);
  *isOpen = m_isOpen;
  m_closedSlot.reset(new ClosedSlot(slot));
}

void MockClientConnection::AcceptConnection() {
  lock_guard<recursive_mutex> lock(m_mutex);
  m_isOpen = true;
  m_connectedCondition.notify_all();
}

void MockClientConnection::RejectConnection() {
  lock_guard<recursive_mutex> lock(m_mutex);
  m_isOpen = false;
  m_connectedCondition.notify_all();
}

void MockClientConnection::CloseConnection() {
  lock_guard<recursive_mutex> lock(m_mutex);
  if(!m_isOpen) {
    return;
  }
  m_serverReader->Break();
  m_clientReader->Break();
  m_isOpen = false;
  if(m_closedSlot == NULL) {
    return;
  }
  shared_ptr<ClosedSlot> closedSlot = m_closedSlot;
  (*closedSlot)();
}

Channel* MockClientConnection::CreateServerChannel() {
  return m_channel->CreateServerChannel();
}
